package com.example.s7;

import java.io.IOException;
import java.util.Arrays;

import HslCommunication.Core.Types.OperateResultExOne;
import HslCommunication.Profinet.Siemens.SiemensPLCS;
import HslCommunication.Profinet.Siemens.SiemensS7Net;
import com.example.exception.OpcException;
import com.github.s7connector.api.DaveArea;
import com.github.s7connector.api.S7Connector;
import com.github.s7connector.api.factory.S7ConnectorFactory;
import lombok.extern.slf4j.Slf4j;

/**
 * Copyright: Copyright (c) 2022
 *
 * <p>
 * Description:
 *
 * @author LiuFeng
 * @version 2022/11/11 13:56
 * @since
 */
@Slf4j
public class PLCUtils {

    private PLCUtils() {
    }

    private volatile static S7Connector connector;

    private volatile static SiemensS7Net siemensTcpNetDB = null;

    private volatile static SiemensS7Net siemensTcpNetSectionOne = null;

    private static String IP_ADDRESS_DB = "172.18.1.101";

    private static String IP_ADDRESS_SECTION_ONE = "172.18.1.90";

    public static S7Connector connect() {

        if (connector == null) {
            synchronized (PLCUtils.class) {
                if (connector == null) {
                    try {
                        closeConnector();
                    } catch (Throwable e) {
                        throw new RuntimeException(e);
                    }
                    //PLC地址
                    String ipAddress = IP_ADDRESS_DB;
                    //默认端口
                    //int port = 102;
                    int rack = 0;
                    int slot = 3;
                    int timeout = 10000;
                    S7Connector s7connector = S7ConnectorFactory.buildTCPConnector().withHost(ipAddress)
                            //.withPort(port) //optional
                            .withRack(rack) //optional
                            .withSlot(slot) //optional
                            .withTimeout(timeout) //连接超时时间
                            .build();
                    connector = s7connector;
                    return s7connector;
                }
            }
        }
        return connector;

    }

    /**
     * 连接IP地址是 172.18.1.101 的PLC
     *
     * @return 连接
     */
    public static SiemensS7Net getSiemensTcpNetDB() {
       /* if (siemensTcpNetDB == null) {
            synchronized (PLCUtils.class) {
                if (siemensTcpNetDB == null) {
                    siemensTcpNetDB = new SiemensS7Net(SiemensPLCS.S400, IP_ADDRESS_DB);
                }
            }
        }*/
        siemensTcpNetDB = new SiemensS7Net(SiemensPLCS.S400, IP_ADDRESS_DB);
        return siemensTcpNetDB;
    }

    /**
     * 连接IP地址是 172.18.1.90 的PLC，CPU是1500
     *
     * @return 连接
     */
    public static SiemensS7Net getSiemensTcpNetSectionOne() {
        if (siemensTcpNetSectionOne == null) {
            synchronized (PLCUtils.class) {
                if (siemensTcpNetSectionOne == null) {
                    siemensTcpNetSectionOne = new SiemensS7Net(SiemensPLCS.S1500, IP_ADDRESS_SECTION_ONE);
                }
            }
        }
        return siemensTcpNetSectionOne;
    }


    /**
     * @param area       DaveArea.DB 表示读取PLC的地址区域为DB
     * @param areaNumber DB块地址，若plc中是DB1000，则填1000
     * @param length     数据长度
     * @param offset     偏移量
     * @return
     */
    public static byte[] readPlcData(DaveArea area, int areaNumber, int length, int offset) {
        connect();
        try {
            byte[] bytes = connector.read(area, areaNumber, length, offset);
            return bytes;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public static byte[] readPlcData(String address, short length) {

        OperateResultExOne<byte[]> resultExOne = siemensTcpNetDB.Read(address, length);
        if (resultExOne.IsSuccess) {
            return resultExOne.Content;
        }
        return null;
    }


    /**
     * 把byte转化成2进制字符串
     *
     * @param b
     * @return
     */
    public static String getBinaryStrFromByte(byte b) {
        String result = "";
        StringBuffer buffer = new StringBuffer();
        byte a = b;
        ;
        for (int i = 0; i < 8; i++) {
            byte c = a;
            a = (byte) (a >> 1);//每移一位如同将10进制数除以2并去掉余数。
            a = (byte) (a << 1);
            if (a == c) {
                buffer.append("0");
                result = "0" + result;
            } else {
                buffer.append("1");
                result = "1" + result;
            }
            a = (byte) (a >> 1);
        }
        return result;
    }

    /**
     * 程序结束时，关闭connector
     *
     * @throws Throwable 异常
     */
    protected static void closeConnector() {
        Runtime runtime = Runtime.getRuntime();
        runtime.addShutdownHook(new Thread() {
            @Override
            public void run() {
                super.run();
                try {
                    if (connector != null) {
                        connector.close();
                    }
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }

            }
        });
    }

    /**
     * 批量读取二标段PLC数据
     * @param address 地址数组 地址格式：DB5.506
     * @param length 对应地址的长度的数组 4
     * @return
     */
    public static byte[] batchReadPLC(String[] address, short[] length) {
        SiemensS7Net siemensS7Net = null;
        try {
            siemensS7Net = PlcSectionTwoConnectorFactory.borrowObject();
            OperateResultExOne<byte[]> resultExOne = siemensS7Net.Read(address, length);
            if ( resultExOne.IsSuccess){
                return resultExOne.Content;
            }else {
                log.error("数据读取异常：{}, 地址是:{}", resultExOne.Message, Arrays.toString(address));
                return null;
            }
        } catch (Exception e) {
            throw new OpcException(e);
        } finally {
            try {
                PlcSectionTwoConnectorFactory.returnObject(siemensS7Net);
            } catch (Exception e) {
                throw new OpcException(e);
            }
        }
    }

      /**
     * 批量读取一标段PLC数据
     * @param address 地址数组
     * @param length 对应地址的长度的数组
     * @return
     */
    public static byte[] batchReadSectionOnePLC(String[] address, short[] length) {
        SiemensS7Net siemensS7Net = null;
        try {
            siemensS7Net = PlcSectionOneConnectorFactory.borrowObject();
            OperateResultExOne<byte[]> resultExOne = siemensS7Net.Read(address, length);
            if ( resultExOne.IsSuccess){
                return resultExOne.Content;
            }else {
                log.error("数据读取异常：{}, 地址是:{}", resultExOne.Message, Arrays.toString(address));
                return null;
            }
        } catch (Exception e) {
            throw new OpcException(e);
        } finally {
            try {
                PlcSectionOneConnectorFactory.returnObject(siemensS7Net);
            } catch (Exception e) {
                throw new OpcException(e);
            }
        }
    }
}
